<!DOCTYPE html>
<html lang="zh-cn">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="ssdut dsa web" />
    <meta name="author" content="" />

    <title>ssdut.DSA/排序/插入排序</title>
    <link rel="stylesheet" href="../assets/css/fonts/linecons/css/linecons.css">
    <link rel="stylesheet" href="../assets/css/fonts/fontawesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="../assets/css/bootstrap.css">
    <link rel="stylesheet" href="../assets/css/xenon-core.css">
    <link rel="stylesheet" href="../assets/css/xenon-forms.css">
    <link rel="stylesheet" href="../assets/css/xenon-components.css">
    <link rel="stylesheet" href="../assets/css/xenon-skins.css">
    <link rel="stylesheet" href="../assets/css/custom.css">
    <link rel="stylesheet" type="text/css" href="../assets/css/stylesheet.css">
    <link rel="stylesheet" type="text/css" href="../assets/css/common.css">
    <link rel="stylesheet" href="../assets/css/viz-1.0.1.css">
    <link rel="stylesheet" href="../assets/css/visual.css">
    <link rel="stylesheet" href="../assets/css/drawgraph.css">


    <script src="../assets/js/jquery-3.3.1.min.js"></script>

</head>

<body class="page-body">
    <div class="page-container">
        <!-- add class"sidebar-collapsed" to close sidebar by default,"chat-visible" to make chat appear always -->

        <!-- Add"fixed" class to make the sidebar fixed always to the browser viewport. -->
        <!-- Adding class"toggle-others" will keep only one menu item open at a time. -->
        <!-- Adding class"collapsed" collapse sidebar root elements and show only icons. -->
        <div class="sidebar-menu toggle-others fixed">

            <div class="sidebar-menu-inner">

                <header class="logo-env">
                    <!-- logo -->
                    <div class="logo">
                        <a href="../" class="logo-expanded">
                            <img src="../assets/images/logo@2x.png" width="80" alt="" />
                        </a>

                        <a href="../" class="logo-collapsed">
                            <img src="../assets/images/logo-collapsed@2x.png" width="40" alt="" />
                        </a>
                    </div>
                  
                </header>
                <ul id="main-menu" class="main-menu">
                    <!-- add class"multiple-expanded" to allow multiple submenus to open -->
                    <!-- class"auto-inherit-active-class" will automatically add"active" class for parent elements who are marked already with class"active" -->
                    <li >
                        <a href="../index.html">
                            <i class="fa-home"></i>
                            <span class="title">首页</span>
                        </a>
                    </li>
                    <li>
                        <a href="../list/ll.html">
                            <i class="fa-chain"></i>
                            <span class="title">线性表</span>
                        </a>
                        <ul>
                            <li>
                                <a href="../list/ll.html">
                                    <span class="title">单链表</span>
                                </a>
                            </li>
                            <li>
                                <a href="../list/stack.html">
                                    <span class="title">栈</span>
                                </a>
                            </li>
                            <li>
                                <a href="../list/queue.html">
                                    <span class="title">队列</span>
                                </a>
                            </li>
                            <li>
                                <a href="../list/dll.html">
                                    <span class="title">双向链表</span>
                                </a>
                            </li>
                            <li>
                                <a href="../list/deque.html">
                                    <span class="title">双端队列</span>
                                </a>
                            </li>

                        </ul>
                    </li>
                    <li>
                        <a href="../tree/bst.html">
                            <i class="fa-sitemap"></i>
                            <span class="title">二叉树</span>
                        </a>
                        <ul>
                            <li>
                                <a href="../tree/bst.html">
                                    <span class="title">二叉搜索树</span>
                                </a>
                            </li>
                            <li>
                                <a href="../tree/avl.html">
                                    <span class="title">平衡二叉搜索树</span>
                                </a>
                            </li>
                            <li>
                                <a href="../tree/heap.html">
                                    <span class="title">堆</span>
                                </a>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="../graph/graphds.html">
                            <i class="fa-share-alt"></i>
                            <span class="title">图</span>
                        </a>
                        <ul>
                            <li>
                                <a href="../graph/graphds.html">
                                    <span class="title">图的数据结构</span>
                                </a>
                            </li>
                            <li>
                                <a href="../graph/dfsbfs.html">
                                    <span class="title">图的遍历</span>
                                </a>
                            </li>
                            <li>
                                <a href="../graph/mst.html">
                                    <span class="title">最小生成树</span>
                                </a>
                            </li>
                            <li>
                                <a href="../graph/sssp.html">
                                    <span class="title">最短路径</span>
                                </a>
                            </li>
                            <li>
                                <a href="../graph/maxflow.html">
                                    <span class="title">网络最大流</span>
                                </a>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="../hashtable/lp.html">
                            <i class="fa-random"></i>
                            <span class="title">散列表</span>                          
                        </a>
                        <ul>
                            <li>
                                <a href="../hashtable/lp.html">
                                    <span class="title">线性探查</span>
                                </a>
                            </li>
                            <li>
                                <a href="../hashtable/qp.html">
                                    <span class="title">二次探查</span>
                                </a>
                            </li>
                            <li>
                                <a href="../hashtable/dh.html">
                                    <span class="title">双散列</span>
                                </a>
                            </li>
                            <li>
                                <a href="../hashtable/sc.html">
                                    <span class="title">分离链接</span>
                                </a>
                            </li>
                        </ul>
                    </li>
                    <li class="active opened">
                        <a href="../sorting/bubble.html" >
                            <i class="fa-sort-amount-asc"></i>
                            <span class="title">排序</span>
                        </a>
                        <ul>
                            <li>
                                <a href="../sorting/bubble.html">
                                    <span class="title">冒泡排序</span>
                                </a>
                            </li>
                            <li>
                                <a href="../sorting/selection.html">
                                    <span class="title">选择排序</span>
                                </a>
                            </li>
                            <li class="active">
                                <a href="../sorting/insertion.html">
                                    <span class="title">插入排序</span>
                                </a>
                            </li>
                            <li>
                                <a href="../sorting/merge.html">
                                    <span class="title">归并排序</span>
                                </a>
                            </li>
                            <li>
                                <a href="../sorting/quick.html">
                                    <span class="title">快速排序</span>
                                </a>
                            </li>
                            <li >
                                <a href="../sorting/radix.html" >
                                    <span class="title">基数排序</span>
                                </a>
                            </li>
                        </ul>
                    </li>             
                </ul>
            </div>
        </div>

        <div class="main-content">

            <!-- User Info, Notifications and Menu Bar -->
          
                <div class="row" style="margin:-30px -30px 0 -30px; background-color: #FFF; padding:15px 10px 10px; ">
                    <div class="col-auto">
                        <ul class="user-info-menu left-links list-inline list-unstyled">
                            <li class="hidden-sm hidden-xs">
                                <a href="#" data-toggle="sidebar">
                                    <i class="fa-bars"></i>
                                </a>
                            </li>
                        </ul>        
                    </div>
                    <!-- Left links for user info navbar -->
                    <div class="col-auto">
                        <h4 id="page-title" >链表</h4>
                    </div>
                    <div class="col-auto me-auto">
                        <div id="current-action" ></div>
                    </div>               
                    <div class="col-auto" >
                                    <!-- Right links for user info navbar -->              
                        <div id="speed-control">
                            减速
                            <div id="speed-input" class="ui-slider ui-corner-all ui-slider-horizontal ui-widget ui-widget-content">
                                <span tabindex="0" class="ui-slider-handle ui-corner-all ui-state-default" style="left: 72.2222%;"></span>
                            </div>
                            加速
                        </div>              
                    </div>
                </div>
            

          

            <div class="row">

             
                <div id="dark-overlay"></div>


                <div id="sort-viz">
                    <svg id="viz-canvas" height="490" width="1000" ></svg>
                    <br>
                    <svg id="viz-counting-sort-secondary-canvas" height="60" width="1000" style="display: none;"></svg>
                    <div id="viz-radix-sort-canvas" style="display: none;">
                    </div>
                </div>
                

                <div id="overlay" hidden="" style="opacity: 0; display: none;"></div>


                <div id="popup" hidden="">
                    <div id="popup-content"></div>
                    <span id="hide-popup" hidden="" style="background-color: rgb(255, 138, 39);">X <u>关闭</u></span>
                </div>
         

                <script>
                    var PHP_DOMAIN ="";

                    // surprise colour!
                    // Referenced to in  home.js and viz.js also
                    var colourArray = ["#52bc69","#d65775" /*"#ed5a7d"*/ ,"#2ebbd1","#d9513c","#fec515","#4b65ba","#ff8a27","#a7d41e"]; // green, pink, blue, red, yellow, indigo, orange, lime

                    function disableScroll() {
                        $('html').css('overflow', 'hidden');
                    }

                    function enableScroll() {
                        $('html').css('overflow', 'visible');
                    }

                    function replaceAll(find, replace, str) {
                        return str.replace(new RegExp(find, 'g'), replace);
                    }

                    function getColours() {
                        var generatedColours = new Array();
                        while (generatedColours.length < 4) {
                            var n = (Math.floor(Math.random() * colourArray.length));
                            if ($.inArray(n, generatedColours) == -1)
                                generatedColours.push(n);
                        }
                        return generatedColours;
                    }

                    function isOn(value, position) {
                        return (value >> position) & 1 === 1;
                    }

                    function customAlert(msg) {
                        $('#custom-alert p').html(msg);
                        var m = -1 * ($('#custom-alert').outerHeight() / 2);
                        $('#custom-alert').css('margin-top', m + 'px');
                        $('#dark-overlay').fadeIn(function() {
                            $('#custom-alert').fadeIn(function() {
                                setTimeout(function() {
                                    $('#custom-alert').fadeOut(function() {
                                        $('#dark-overlay').fadeOut();
                                    });
                                }, 1000);
                            });
                        });
                    }

                    function showLoadingScreen() {
                        $('#loading-overlay').show();
                        $('#loading-message').show();
                    }

                    function hideLoadingScreen() {
                        $('#loading-overlay').hide();
                    }

                    function commonAction(retval, msg) {
                        //setTimeout(function() {
                        if (retval) { // mode =="exploration" && // now not only for exploration mode, but check if this opens other problems
                         
                            $('#current-action').html(mode =="exploration" ? msg : ("e-Lecture Example (auto play until done)<br>" + msg));
                            $('#progress-bar').slider("option","max", gw.getTotalIteration() - 1);
                            triggerRightPanels();
                            isPlaying = true;
                        }
                        //}, 500);
                    }

                    function getQueryVariable(variable) {
                        var query = window.location.search.substring(1);
                        var vars = query.split('&');
                        for (var i = 0; i < vars.length; i++) {
                            var pair = vars[i].split('=');
                            if (decodeURIComponent(pair[0]) == variable)
                                return decodeURIComponent(pair[1]);
                        }
                        return"";
                    }

                    var generatedColours = getColours();
                    var surpriseColour = colourArray[generatedColours[0]];
                    var colourTheSecond = colourArray[generatedColours[1]];
                    var colourTheThird = colourArray[generatedColours[2]];
                    var colourTheFourth = colourArray[generatedColours[3]];

                    $(function() {
                       

                        // title
                        $('#title a').click(function() {
                            $('#title a').removeClass('selected-viz');
                            $(this).addClass('selected-viz');
                            // temporary quick fix for Google Chrome Aug 2016 issue...
                            setTimeout(function() {
                                document.body.style.zoom ="100.1%";
                            }, 100); // force resize/redraw...
                            setTimeout(function() {
                                document.body.style.zoom ="100%";
                            }, 600);
                        });

                        // overlays stuffs
                        $('#trigger-about').click(function() {
                            if ($(window).width() > 600) {
                                $('#dark-overlay').fadeIn(function() {
                                    $('#about').fadeIn();
                                });
                            } else
                                alert('Sorry, this dialog is too big. Please load it on bigger screen');
                        });

                        $('#trigger-team').click(function() {
                            if ($(window).width() > 600) {
                                $('#dark-overlay').fadeIn(function() {
                                    $('#team').fadeIn();
                                });
                            } else
                                alert('Sorry, this dialog is too big. Please load it on bigger screen');
                        });

                        $('#trigger-terms').click(function() {
                            if ($(window).width() > 600) {
                                $('#dark-overlay').fadeIn(function() {
                                    $('#termsofuse').fadeIn();
                                });
                            } else
                                alert('Sorry, this dialog is too big. Please load it on bigger screen');
                        });

                        $('.close-overlay').click(function() {
                            $('.overlays').fadeOut(function() {
                                $('#dark-overlay').fadeOut();
                            });
                        });

                        $('#dark-overlay').click(function() {
                            $('.overlays').fadeOut();
                            $('#dark-overlay').fadeOut();
                        });
                    });
                </script>




                <script>
                    window.onpopstate = function(event) {
                        var slide = event.state['slide'];
                        openSlide(slide, function() {
                            runSlide(slide);
                        });
                    };

                    function getUrlParameter(sParam) {
                        var sPageURL = decodeURIComponent(window.location.search.substring(1)),
                            sURLVariables = sPageURL.split('&'),
                            sParameterName, i;

                        for (i = 0; i < sURLVariables.length; i++) {
                            sParameterName = sURLVariables[i].split('=');
                            if (sParameterName[0] === sParam) return sParameterName[1] === undefined ? true : sParameterName[1];
                        }
                    };

                    function pushState(slideValue) {
                        var url = '/zh/list';
                        if (typeof slideValue != 'undefined' && slideValue != null) url += '?slide=' + slideValue;
                        window.history.pushState({
                            slide: slideValue
                        },"slide" + slideValue, url);
                    }

                    function showPopup(callback) {
                        $('#popup').fadeIn(100, callback);
                    }

                    function hidePopup(callback) {
                        $('#popup').fadeOut(100, callback);
                    }

                    $(function() {
                        var slide = getUrlParameter('slide');

          

                        $('.mcq-submit').click(function() {
                            var questionId = parseInt($(this).attr('id').split('-')[1]);
                            var answer = $('#mcq-answer-' + questionId).val();
                            var userAnswer = $('input[type=radio][name=mcq-' + questionId + '-choice]:checked').val();

                            if (answer === userAnswer) {
                                $('#answer-status-' + questionId).html('<font color="green"><b>Correct!</b></font>');
                            } else {
                                $('#answer-status-' + questionId).html('<font color="red"><b>Wrong Answer! Try again...</b></font>');
                            }
                            $('#answer-status-' + questionId).show();
                            setTimeout(function() {
                                $('#answer-status-' + questionId).fadeOut(1000);
                            }, 1000);
                        });

                        $('.msq-submit').click(function() {
                            var questionId = parseInt($(this).attr('id').split('-')[1]);
                            var answer = $('#msq-answer-' + questionId).val();

                            var answers = [];
                            $('input[type=checkbox][class=msq-choice]:checked').each(function() {
                                answers.push($(this).attr('id').split('-')[3]);
                            });
                            answers.sort();
                            var userAnswer = answers.join(',');

                            if (answer === userAnswer) {
                                $('#answer-status-' + questionId).html('<font color="green">Correct!</font>');
                            } else {
                                $('#answer-status-' + questionId).html('<font color="red">Wrong Answer! Try again...</font>');
                            }
                            $('#answer-status-' + questionId).show();
                            setTimeout(function() {
                                $('#answer-status-' + questionId).fadeOut(1000);
                            }, 1000);
                        });

                      

                        $('#hide-popup').click(function() {
                            hidePopup();
                        });

                        $('#popup').hover(function() {
                            $('#hide-popup').show();
                        }, function() {
                            $('#hide-popup').hide();
                        });




                        // temporary quick fix for Google Chrome Aug 2016 issue..., put at last part so that everything else has been loaded
                        // setTimeout(function(){ document.body.style.zoom ="100.1%"; }, 500);
                        // setTimeout(function(){ document.body.style.zoom ="100%"; }, 600);
                        // I turn it off on 14 June 2018, seems 'ok'?
                    });

               

                    function adjustPopupToImageSize() {
                        var width = $('#popup-image').prop('width');
                        var height = $('#popup-image').prop('height');
                        $('#popup').width(width + 20);
                        $('#popup').height(height + 20);
                        if (width == 0 && height == 0) {
                            setTimeout(adjustPopupToImageSize, 200);
                        } else {
                            showPopup();
                        }
                    }

                    function POPUP_IMAGE(url) {
                        $('#popup-content').html('<img id="popup-image" src="' + url + '">');
                        adjustPopupToImageSize();
                    }

                    function URL(url) {
                        window.open(url, '_blank');
                    }

                </script>


            </div>






            <!-- Main Footer -->
            <!-- Choose between footer styles:"footer-type-1" or"footer-type-2" -->
            <!-- Add class"sticky" to  always stick the footer to the end of page (if page contents is small) -->
            <!-- Or class"fixed" to  always fix the footer to the end of page -->
            <footer class="main-footer sticky footer-type-2 sticky fixed">

                <div class="footer-inner">
                    <div class="row">
                        <div class="col-auto">
                            <div class="btn-group dropdown dropup">                               
                                <a id="actions-toggle" class="btn btn-sm btn-info dropdown-toggle " data-bs-toggle="dropdown"  data-bs-auto-close="outside" aria-haspopup="true" aria-expanded="false">                            
                                    操作                                                      
                                </a>                                    
                                <div id="actions" class="dropdown-menu">
                                    <div class="dropdown dropend">
                                        <a  class="dropdown-item dropdown-toggle"  data-bs-toggle="dropdown" aria-haspopup="true"   aria-expanded="false">
                                            创建</a>
                                        <div class="dropdown-menu " >
                                            <div  class="dropdown-item" onclick="createList('random')" >
                                                <a>随机</a>
                                            </div>
                                            <div class="dropdown-divider"></div>
                                            
                                            <div  class="dropdown-item" onclick="createList('sorted-increasing')" >
                                                <a>有序-升序</a>
                                            </div>
                                            <div  class="dropdown-item" onclick="createList('sorted-decreasing')" >
                                                <a>有序-降序</a>
                                            </div>                                        
                                            <div class="dropdown-divider"></div>
                                            <div  class="dropdown-item" onclick="createList('nearly-sorted-increasing')" >
                                                <a>近似有序-升序</a>
                                            </div> 
                                            <div  class="dropdown-item" onclick="createList('nearly-sorted-decreasing')" >
                                                <a>近似有序-降序</a>
                                            </div>  
                                            <div class="dropdown-divider"></div> 
                                            <div id="create-random-fixed-size" class="dropdown-header" style="min-width: 200px;">
                                                <p>搜索值</p>
                                            </div>    
                                            <div id="" class="  dropdown-item-text" >                          
                                                
                                                <input type="text" id="userdefined-input" title="Enter a list of numbers, separated by commas." autocomplete="off" value="3,44,38,5,47,15,36,26,27,2,46,4,19,50,48" spellcheck="false" >
                                                <div id="search-go" class="btn btn-sm btn-blue  " onclick="createList('userdefined')">
                                                    执行</div>
                                            </div>                                 
                                        </div>  
                                    </div>
                                    <div class="dropdown-item" onclick="sort();">
                                        <a>排序</a>
                                    </div>
                         
                                </div>
                            </div>
                        </div>
                        
                        <div class="col-auto">
                            <span id="go-to-beginning" class="media-control-button" title="go to beginning" onclick="goToBeginning()"><img
                                src="../assets/images/goToBeginning.png" alt="go to beginning"></span>
                        </div>
                        <div class="col-auto">
                            <span id="previous" class="media-control-button" title="step backward" onclick="stepBackward()"><img
                                src="../assets/images/prevFrame.png" alt="previous frame"></span>
                        </div>
                        <div class="col-auto">
                            <span id="pause" class="media-control-button" title="pause" onclick="pause()" ><img
                                src="../assets/images/pause.png" alt="pause"></span>
                            <span id="play" class="media-control-button" title="play" onclick="play()" style="display: none;">
                                <img src="../assets/images/play.png" alt="play"></span>
                        </div>
                        <div class="col-auto">
                            <span id="next" class="media-control-button" title="step forward" onclick="stepForward()"><img
                                src="../assets/images/nextFrame.png" alt="next frame"></span>
                        </div>
                        <div class="col-auto">
                            <span id="go-to-end" class="media-control-button" title="go to end" onclick="goToEnd()"><img
                                src="../assets/images/goToEnd.png" alt="go to end"></span>
                        </div>


                        <div class="col-5 me-auto">
                            <div id="progress-bar" class="slider ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all" data-min="0" data-max="1800" data-value="223">
                            </div>

                        </div>
                        <div class="col-auto">
                            <div class="btn-group dropdown dropup">
                                <a id="codetrace-toggle" class="btn btn-sm btn-warning dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="false" aria-expanded="false" data-bs-offset="0,20">                            
                                    代码跟踪
                                                  
                                </a>
                                <div id="codetrace-panel" class="dropdown-menu dropdown-menu-end">
                                    <div class="row" style="width: 600px;">
                                        <div class="col-sm-12">
                                            <div id="status" class=" col-sm-12" >
                                                <p></p>
                                            </div>
                                            <div id="codetrace" class=" col-sm-12">
                                                <p id="code1" style="padding-top: 10px; background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                                <p id="code2" style="background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                                <p id="code3" style="background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                                <p id="code4" style="background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                                <p id="code5" style="background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                                <p id="code6" style="background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                                <p id="code7" style="padding-bottom: 10px; background-color: rgb(82, 188, 105); color: rgb(255, 255, 255);"></p>
                                            </div>
                                        </div>
                                    </div>
                                </div>

                            </div>
                        </div>
                       </div>
            </footer>
        </div>
    </div>    

    <div class="page-loading-overlay">
        <div class="loader-2"></div>
    </div>

  


    <!-- Bottom Scripts -->
    <script src="../assets/js/bootstrap.bundle.js"></script>
    <script src="../assets/js/bootstrap5.dropdown.ml.hack.js"></script>
    <script src="../assets/js/TweenMax.min.js"></script>
    <script src="../assets/js/resizeable.js"></script>
    <script src="../assets/js/joinable.js"></script>
    <script src="../assets/js/xenon-api.js"></script>
    <script src="../assets/js/xenon-toggles.js"></script>


    <!-- Imported scripts on this page -->
    <script src="../assets/js/xenon-widgets.js"></script>
    <script src="../assets/js/devexpress-web-14.1/js/globalize.min.js"></script>
    <!--   <script src="../assets/js/devexpress-web-14.1/js/dx.chartjs.js"></script>-->
    <script src="../assets/js/toastr/toastr.min.js"></script>


    
    <script src="../assets/js/jquery-ui/jquery-ui.js"></script>
    <script src="../assets/js/knob/jquery.knob.min.js"></script>

    <!-- JavaScripts initializations and stuff -->
    <script src="../assets/js/xenon-custom.js"></script>
    
    <script src="../assets/js/d3.min.js"></script>
    <script src="../assets/js/viz-1.0.3.js"></script>
    <script src="../assets/js/visualgo_print.js"></script>
    <script src="../assets/js/graph_library.js"></script>
    <script type="text/javascript">
        // Sorting Widget
        // original author: Ian Leow Tze Wei
        
        var Sorting = function() {
          // constants
          var HIGHLIGHT_NONE = "lightblue";
          var HIGHLIGHT_STANDARD = "green";
          var HIGHLIGHT_SPECIAL = "#DC143C";
          var HIGHLIGHT_SORTED = "orange";
        
          var HIGHLIGHT_LEFT = "#3CB371";
          var HIGHLIGHT_RIGHT = "#9932CC";
          var HIGHLIGHT_PIVOT = "yellow";
        
          var HIGHLIGHT_GRAY = "#CCCCCC";
        
          var HIGHLIGHT_RAINBOW = [
            "#FF0000",
            "#FF4000",
            "#FF8000",
            "#FFBF00",
            "#FFFF00",
            "#BFFF00",
            "#80FF00",
            "#40FF00",
            //"#00FF00",
            "#00FF40",
            "#00FF80",
            "#00FFBF",
            "#00FFFF",
            "#00BFFF",
            "#0080FF",
            "#0040FF",
            "#0000FF",
            "#4000FF",
            "#8000FF",
            "#BF00FF",
            "#FF00FF"
          ];
        
          var HIGHLIGHT_BLUESHADES = [
            HIGHLIGHT_GRAY,
            HIGHLIGHT_NONE,
            "#9DC4E8",
            "#8EB1EB",
            "#7E9DED",
            "#6E89EF",
            "#5E76F1",
            "#4F62F4",
            "#3F4FF6",
            "#2F3BF8",
            "#1F27FA",
            "#1014FD",
            "#0000FF",
            "#0000FF",
            "#0000FF",
            "#0000FF",
            "#0000FF",
            "#0000FF",
            "#0000FF",
            "#0000FF",
            "#0000FF"
          ];
        
          var POSITION_USE_PRIMARY = "a";
          var POSITION_USE_SECONDARY_IN_DEFAULT_POSITION = "b";
        
          // Objects definition
          var Entry = function(value, highlight, position, secondaryPositionStatus) {
            this.value = value; // number
            this.highlight = highlight; // string, use HIGHLIGHT_ constants
            this.position = position; // number
            this.secondaryPositionStatus = secondaryPositionStatus; // integer, +ve for position overwrite, -ve for absolute postion (-1 for 0th absolution position)
          }
        
          var Backlink = function(value, highlight, entryPosition, secondaryPositionStatus) {
            this.value = value; // number
            this.highlight = highlight; // string, use HIGHLIGHT_ constants
            this.entryPosition = entryPosition; // number
            this.secondaryPositionStatus = secondaryPositionStatus; // integer, +ve for position overwrite
          }
        
          var State = function(entries, backlinks, barsCountOffset, status, lineNo) {
            this.entries = entries; // array of Entry's
            this.backlinks = backlinks; // array of Backlink's
            this.barsCountOffset = barsCountOffset; // how many bars to "disregard" (+ve) or to "imagine" (-ve) w.r.t. state.entries.length when calculating the centre position
            this.status = status;
            this.lineNo = lineNo; //integer or array, line of the code to highlight
          }
        
          //Helpers
          var EntryBacklinkHelper = new Object();
          EntryBacklinkHelper.appendList = function(entries, backlinks, numArray) {
            for (var i = 0; i < numArray.length; i++) {
              EntryBacklinkHelper.append(entries, backlinks, numArray[i]);
            }
          }
        
          EntryBacklinkHelper.append = function(entries, backlinks, newNumber) {
            entries.push(new Entry(newNumber, HIGHLIGHT_NONE, entries.length, POSITION_USE_PRIMARY));
            backlinks.push(new Backlink(newNumber, HIGHLIGHT_NONE, backlinks.length, POSITION_USE_PRIMARY));
          }
        
          EntryBacklinkHelper.update = function(entries, backlinks) {
            for (var i = 0; i < backlinks.length; i++) {
              entries[backlinks[i].entryPosition].highlight = backlinks[i].highlight;
              entries[backlinks[i].entryPosition].position = i;
              entries[backlinks[i].entryPosition].secondaryPositionStatus = backlinks[i].secondaryPositionStatus;
            }
          }
        
          EntryBacklinkHelper.copyEntry = function(oldEntry) {
            return new Entry(oldEntry.value, oldEntry.highlight, oldEntry.position, oldEntry.secondaryPositionStatus);
          }
        
          EntryBacklinkHelper.copyBacklink = function(oldBacklink) {
            return new Backlink(oldBacklink.value, oldBacklink.highlight, oldBacklink.entryPosition, oldBacklink.secondaryPositionStatus);
          }
        
          EntryBacklinkHelper.swapBacklinks = function(backlinks, i, j) {
            var swaptemp = backlinks[i];
            backlinks[i] = backlinks[j];
            backlinks[j] = swaptemp;
          }
        
          var StateHelper = new Object();
          StateHelper.createNewState = function(numArray) {
            var entries = new Array();
            var backlinks = new Array();
            EntryBacklinkHelper.appendList(entries, backlinks, numArray);
            return new State(entries, backlinks, 0, "", 0);
          }
        
          StateHelper.copyState = function(oldState) {
            var newEntries = new Array();
            var newBacklinks = new Array();
            for (var i = 0; i < oldState.backlinks.length; i++) {
              newEntries.push(EntryBacklinkHelper.copyEntry(oldState.entries[i]));
              newBacklinks.push(EntryBacklinkHelper.copyBacklink(oldState.backlinks[i]));
            }
        
            var newLineNo = oldState.lineNo;
            if (newLineNo instanceof Array)
              newLineNo = oldState.lineNo.slice();
        
            return new State(newEntries, newBacklinks, oldState.barsCountOffset, oldState.status, newLineNo);
          }
        
          StateHelper.updateCopyPush = function(list, stateToPush) {
            EntryBacklinkHelper.update(stateToPush.entries, stateToPush.backlinks);
            list.push(StateHelper.copyState(stateToPush));
          }
        
          var FunctionList = new Object();
          FunctionList.text_y = function(d) {
            var barHeight = scaler(d.value);
            if (barHeight < 32) return -15;
            return barHeight-15;
          }
        
          FunctionList.g_transform = function(d) {
            if (d.secondaryPositionStatus == POSITION_USE_PRIMARY)
              return 'translate(' + (centreBarsOffset + d.position * barWidth) + ", " + (maxHeight - scaler(d.value)) + ')';
            else if (d.secondaryPositionStatus == POSITION_USE_SECONDARY_IN_DEFAULT_POSITION)
              return 'translate(' + (centreBarsOffset + d.position * barWidth) + ", " + (maxHeight * 2 + gapBetweenPrimaryAndSecondaryRows - scaler(d.value)) + ')';
            else if (d.secondaryPositionStatus >= 0)
              return 'translate(' + (centreBarsOffset + d.secondaryPositionStatus * barWidth) + ", " + (maxHeight * 2 + gapBetweenPrimaryAndSecondaryRows - scaler(d.value)) + ')';
            else if (d.secondaryPositionStatus < 0)
              return 'translate(' + ((d.secondaryPositionStatus * -1 - 1) * barWidth) + ", " + (maxHeight * 2 + gapBetweenPrimaryAndSecondaryRows - scaler(d.value)) + ')';
            else
              return 'translation(0, 0)'; // error
          }
        
          FunctionList.radixElement_left = function(d) {
            if (d.secondaryPositionStatus == POSITION_USE_PRIMARY)
              return d.position * 65 + centreBarsOffset + "px";
            return d.secondaryPositionStatus * 65 + 17.5 + "px";
          }
        
          FunctionList.radixElement_bottom = function(d, i) {
            if (d.secondaryPositionStatus == POSITION_USE_PRIMARY)
              return 500 - 24 + "px";
            //console.log(i + " " + radixSortBucketOrdering[i]);
            return radixSortBucketOrdering[i] * 30 + 5 + "px";
          }
        
          FunctionList.radixElement_html = function(d) {
            if (d.highlight == HIGHLIGHT_NONE)
              return d.value;
        
            var text = "" + d.value;
            while (text.length != 4)
              text = " " + text;
        
            var positionToHighlight = 0; //positionToHighlight = log_to_base_10(d.highlight), assumes d.highlight is power of 10
            var positionCounter = d.highlight;
            while (positionCounter != 1) {
              positionToHighlight++;
              positionCounter /= 10;
            }
        
            positionToHighlight = 3-positionToHighlight;
        
            if (text.charAt(positionToHighlight) != " ") {
              text = text.slice(0, positionToHighlight) +
                     "<span style='color: #B40404;'>" +
                     text.charAt(positionToHighlight) +
                     "</span>" +
                     text.slice(positionToHighlight+1);
            }
        
            text = text.trim();
            return text;
          }
        
          var makePaler = function(hexColor) {
            var red = Math.floor(parseInt(hexColor.slice(1, 3), 16) + 150);
            var green = Math.floor(parseInt(hexColor.slice(3, 5), 16) + 150);
            var blue = Math.floor(parseInt(hexColor.slice(5, 7), 16) + 150);
        
            if (red > 255) red = 255;
            if (green > 255) green = 255;
            if (blue > 255) blue = 255;
        
            red = red.toString(16);
            green = green.toString(16);
            blue = blue.toString(16);
        
            if (red.length == 1) red = "0" + red;
            if (green.length == 1) green = "0" + green;
            if (blue.length == 1) blue = "0" + blue;
            return "#" + red + green + blue;
          }
        
          // Variables/Settings
          this.currentNumList = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]; // the default
        
          var barWidth = 50;
          var maxHeight = 230;
          var gapBetweenBars = 5;
          var maxNumOfElements = 20; //max 20 elements currently
          var gapBetweenPrimaryAndSecondaryRows = 30; // of the bars
        
          var maxCountingSortElementValue = 9; // Note that this isn't really customizable, as the code for counting sort is written with this value = 9 in mind.
          var maxRadixSortElementValue = 9999; // Note that this isn't really customizable, as the code for radix sort is written with this value = 9999 in mind.
          var maxElementValue = 50; // for all other sorts - this is fully customizable (seriously)
        
          var graphElementSize = 10; // The width of the square in the side-graph representing 1 element
          var graphElementGap = 2; // The width of the gap between each element in the side-graph
          var graphRowGap = 10; // The height of the gap between each row in the side graph
        
          //Code body
          var statelist = new Array();
          var secondaryStatelist = new Array();
          var transitionTime = 500;
          var currentStep = 0;
          var animInterval;
          var issPlaying; //so named so as not to mess with the isPlaying in viz.js
        
          var quickSortUseRandomizedPivot; //true-false flag
          var mergeSortInversionIndexCounter; //used by merge sort to count the inversion inde
          var centreBarsOffset; // x offset to centre the bars in the canvas
          var radixSortBucketOrdering; // used to order the elements inside each bucket (for radix sort). for formatting purposes.
        
          var isRadixSort = false;
          var isCountingSort = false;
        
          this.selectedSortFunction;
          // this.useEnhancedBubbleSort = false;
          this.computeInversionIndex = false;
        
          var canvas = d3.select("#viz-canvas")
                         .attr("height", maxHeight * 2 + gapBetweenPrimaryAndSecondaryRows)
                         .attr("width", barWidth * maxNumOfElements);
        
          var countingSortSecondaryCanvas = d3.select("#viz-counting-sort-secondary-canvas")
                                              .attr("height", 60)
                                              .attr("width", barWidth * maxNumOfElements);
        
          var radixSortCanvas = d3.select("#viz-radix-sort-canvas");
        
          var scaler = d3.scale
                         .linear()
                         .range([0, maxHeight]);
        
          var drawState = function(stateIndex) {
            if (isRadixSort)
              drawRadixSortCanvas(statelist[stateIndex], secondaryStatelist[stateIndex]);
            else
              drawBars(statelist[stateIndex]);
        
            $('#status p').html(statelist[stateIndex].status);
            highlightLine(statelist[stateIndex].lineNo);
        
            if (isCountingSort)
              drawCountingSortCounters(secondaryStatelist[stateIndex]);
          };
        
          var drawBars = function(state) {
            scaler.domain([0, d3.max(state.entries, function(d) {
              return d.value;
            })]);
        
            centreBarsOffset = (maxNumOfElements - (state.entries.length - state.barsCountOffset)) * barWidth / 2;
        
            var canvasData = canvas.selectAll("g").data(state.entries);
        
            // Exit ==============================
            var exitData = canvasData.exit()
                                     .remove();
        
            // Entry ==============================
            var newData = canvasData.enter()
                                    .append("g")
                                    .attr("transform", FunctionList.g_transform);
        
            newData.append("rect")
                   .attr("height", 0)
                   .attr("width", 0);
        
            newData.append("text")
                   .attr("dy", ".35em")
                   .attr("x", (barWidth - gapBetweenBars) / 2)
                   .attr("y", FunctionList.text_y)
                   .text(function(d) {
                     return d.value;
                   });
        
            // Update ==============================
            canvasData.select("text")
                      .transition()
                      .attr("y", FunctionList.text_y)
                      .text(function(d) {
                        return d.value;
                      });
        
            canvasData.select("rect")
                      .transition()
                      .attr("height", function(d) {
                        return scaler(d.value);
                      })
                      .attr("width", barWidth - gapBetweenBars)
                      .style("fill", function(d) {
                        return d.highlight;
                      });
        
            canvasData.transition()
                      .attr("transform", FunctionList.g_transform)
          };
        
          var drawCountingSortCounters = function(state) {
            var canvasData;
            if (state == null)
              canvasData = countingSortSecondaryCanvas.selectAll("text").data([]);
            else
              canvasData = countingSortSecondaryCanvas.selectAll("text").data(state);
        
            // Exit ==============================
            var exitData = canvasData
                    .exit()
                    .remove();
        
            // Entry ==============================
        
            var newData = canvasData
                    .enter()
                    .append("text")
                    .attr("dy", ".35em")
                    .attr("x", function(d, i) {
                      return (i + 5) * barWidth + (barWidth - gapBetweenBars) / 2;
                    })
                    .attr("y", 20)
                    .text(function(d) {
                      return d;
                    });
        
            // Update ==============================
        
            canvasData
                    .transition()
                    .text(function(d) {
                      return d;
                    });
          };
        
          var drawRadixSortCanvas = function(state, secondaryState) {
            centreBarsOffset = (1000 - (state.entries.length * 65 - 10)) / 2; //uh, it's not really bars now, but just reusing the variable - same concept still
        
            var canvasData = radixSortCanvas.selectAll("div").data(state.entries);
            var radixSortBucketCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
            radixSortBucketOrdering = new Array(state.backlinks.length);
        
            for (var i = 0; i < state.backlinks.length; i++) {
              if (state.backlinks.secondaryPositionStatus != POSITION_USE_PRIMARY)
                radixSortBucketOrdering[state.backlinks[i].entryPosition] = radixSortBucketCount[state.backlinks[i].secondaryPositionStatus]++;
            }
        
            // Handle the buckets' DIV's
            if (secondaryState)
              $("#radix-sort-bucket-labels-collection").show();
            else
              $("#radix-sort-bucket-labels-collection").hide();
        
            // Exit ==============================
            var exitData = canvasData.exit()
                                     .remove();
        
            // Entry ==============================
            var newData = canvasData.enter()
                                    .append("div")
                                    .classed({"radix-sort-element": true})
                                    .style({
                                      "left": FunctionList.radixElement_left,
                                      "bottom": FunctionList.radixElement_bottom
                                    })
                                    .html(FunctionList.radixElement_html);
        
            // Update ==============================
            canvasData.html(FunctionList.radixElement_html)
                      .transition()
                      .style({
                        "left": FunctionList.radixElement_left,
                        "bottom": FunctionList.radixElement_bottom
                      });
          };
        
          var generateRandomNumberArray = function(size, limit) {
            var numArray = new Array();
            for (var i = 0; i < size; i++) {
              numArray.push(generateRandomNumber(1, limit));
            }
            return numArray;
          };
        
          var generateRandomNumber = function(min, max) { //generates a random integer between min and max (both inclusive)
            return Math.floor(Math.random() * (max - min + 1)) + min;
          };
        
          var convertToNumber = function(num) {
            return +num;
          };
        
          this.createList = function(type) {
            var numArrayMaxListSize = 20;
            var numArrayMaxElementValue = maxElementValue;
            if (this.selectedSortFunction == this.radixSort) {
              numArrayMaxListSize = 15;
              numArrayMaxElementValue = maxRadixSortElementValue;
            }
            else if (this.selectedSortFunction == this.countingSort) {
              numArrayMaxElementValue = maxCountingSortElementValue;
            }
        
            var numArray = generateRandomNumberArray(generateRandomNumber(10, numArrayMaxListSize), numArrayMaxElementValue);
        
            switch (type) {
              case 'userdefined':
                numArray = $('#userdefined-input').val().split(",");
        
                if (numArray.length > numArrayMaxListSize) {
                  toastr.error('<span style="white-space: normal;">&nbsp;不能多于{maxSize} 个元素&nbsp;</span>'.replace("{maxSize}", numArrayMaxListSize));
                  return false;
                }
        
                for (var i = 0; i < numArray.length; i++) {
                  var temp = convertToNumber(numArray[i]);
        
                  if (numArray[i].trim() == "") {
                    toastr.error('似乎缺少了一个元素（也许某个地方有重复的逗号？）');
                    return false;
                  }
                  if (isNaN(temp)) {
                    toastr.error('<span style="white-space: normal;">&nbsp;似乎有一个无效的元素（非数字）{num}</span>'.replace("{num}", numArray[i]));
                    return false;
                  }
                  if (temp < 1 || temp > numArrayMaxElementValue) {
                    toastr.error('抱歉，值域限制在1和<span style="white-space: normal;">&nbsp;{maxValue} 之间（包括两界）。（超出值域的值: {num} ）</span>'.replace("{maxValue}", numArrayMaxElementValue).replace("{num}", numArray[i]));
                    return false;
                  }
        
                  numArray[i] = convertToNumber(numArray[i]);
                }
                break;
              case 'random':
                break;
              case 'sorted-increasing':
              case 'nearly-sorted-increasing':
                numArray.sort(d3.ascending);
                break;
              case 'sorted-decreasing':
              case 'nearly-sorted-decreasing':
                numArray.sort(d3.descending);
                break;
            }
        
            if (type.indexOf("nearly") != -1) {
              // To make the list nearly sorted, we take the already sorted list and make swaps
              // such that the list becomes not sorted. The number of such swaps varies from 1 to 2 (customizable).
              // The idea is that the more swaps we make, the less "sorted" the list is.
              //
              // Another limitation is that each swap occurs between elements that are at most 3 positions away.
              while (true) {
                var newNumArray = numArray.slice();
        
                var numOfSwaps = generateRandomNumber(1, 2);
                for (var i = 0; i < numOfSwaps; i++) {
                  var firstSwappingIndex = generateRandomNumber(0, newNumArray.length - 4);
                  var secondSwappingIndex = generateRandomNumber(1, 3) + firstSwappingIndex;
        
                  var temp = numArray[firstSwappingIndex];
                  newNumArray[firstSwappingIndex] = numArray[secondSwappingIndex];
                  newNumArray[secondSwappingIndex] = temp;
                }
        
                // We compare the numArray with newNumArray, if they're are the same,
                // we try again, else we reassign numArray to newNumArray and break.
                var isEquals = true;
                for (var i = 0; i < numArray.length; i++) {
                  if (numArray[i] != newNumArray[i]) {
                    isEquals = false;
                    break;
                  }
                }
        
                if (!isEquals) {
                  numArray = newNumArray;
                  break;
                }
              }
            }
        
            this.loadNumberList(numArray);      
          }
        
          this.loadNumberList = function(numArray) {
  
        
            issPlaying = false;
            currentStep = 0;
            this.currentNumList = numArray;
        
            //console.log("numArray: " + numArray);
        
            statelist = [StateHelper.createNewState(numArray)];
            secondaryStatelist = [null]; // the initial secondary state will be an empty state
            drawState(0);
          }
        
          this.setSelectedSortFunction = function(f) {
            this.selectedSortFunction = f;
            isRadixSort = (this.selectedSortFunction == this.radixSort);
            isCountingSort = (this.selectedSortFunction == this.countingSort);
          }
        
          this.sort = function(callback) {
            return this.selectedSortFunction(callback);
          }
        
          this.radixSort = function(callback) {
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[0]);
        
            populatePseudocode([
            'create 10 buckets (queues) for each digit (0 to 9)',
            'for each digit placing',
            '  for each element in list',
            '    move element into respective bucket',
            '  for each bucket, starting from smallest digit',
            '    while bucket is non-empty',
            '      restore element to list'
            ]);
        
            secondaryStatelist = [false]; // showBucket flag - if true, shows the DIV's representing the bucketss
            var currentPlacing = 1;
            var targetPlacing = 1;
            var backlinkBuckets = [[], [], [], [], [], [], [], [], [], []];
        
            var maxValue = d3.max(state.backlinks, function(d) {
              return d.value;
            });
            while (maxValue >= 10) {
              targetPlacing *= 10;
              maxValue = Math.floor(maxValue / 10);
            }
        
            for (; currentPlacing <= targetPlacing; currentPlacing *= 10) {
              for (var i = 0; i < numElements; i++)
                state.backlinks[i].highlight = currentPlacing;
        
              StateHelper.updateCopyPush(statelist, state);
              secondaryStatelist.push(true);
        
              for (var i = 0; i < numElements; i++) {
                var currentDigit = Math.floor(state.backlinks[i].value / currentPlacing) % 10;
                state.backlinks[i].secondaryPositionStatus = currentDigit;
                backlinkBuckets[currentDigit].push(state.backlinks[i]);
                StateHelper.updateCopyPush(statelist, state);
                secondaryStatelist.push(true);
              }
        
              for (var i = 0, j = 0; i <= 9; ) {
                if (backlinkBuckets[i].length == 0) {
                  i++;
                  continue;
                }
        
                state.backlinks[j++] = backlinkBuckets[i].shift();
              }
        
              for (var i = 0; i < numElements; i++) {
                state.backlinks[i].secondaryPositionStatus = POSITION_USE_PRIMARY;
                StateHelper.updateCopyPush(statelist, state);
                secondaryStatelist.push(true);
              }
            }
        
            for (var i = 0; i < numElements; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE;
            StateHelper.updateCopyPush(statelist, state);
            secondaryStatelist.push(false);
        
            this.play(callback);
            return true;
          }
        
          this.countingSort = function(callback) {
            // Note that while we have the maxCountingSortElementValue variable, it isn't really customizable.
            // The code here written is really just for the range 1 to 9.
        
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[0]);
        
            populatePseudocode([
            'create key (counting) array',
            'for each element in list',
            '  increase the respective counter by 1',
            'for each counter, starting from smallest key',
            '  while counter is non-zero',
            '    restore element to list',
            '    decrease counter by 1'
            ]);
        
            var secondaryState = [0, 0, 0, 0, 0, 0, 0, 0, 0];
            var backlinkBuckets = [[], [], [], [], [], [], [], [], []];
        
            state.barsCountOffset = maxCountingSortElementValue;
        
            for (var i = 1; i <= maxCountingSortElementValue; i++) {
              EntryBacklinkHelper.append(state.entries, state.backlinks, i);
              state.backlinks[numElements + i - 1].highlight = HIGHLIGHT_GRAY;
              state.backlinks[numElements + i - 1].secondaryPositionStatus = i * -1 - 5;
            }
        
            state.lineNo = 1;
            state.status = '创建一个关键值（计数）数组（从1到9）.';
        
            StateHelper.updateCopyPush(statelist, state);
            secondaryStatelist.push(secondaryState.slice()); // copy the array and push it into the secondary statelist
        
            for (var i = 0; i < numElements; i++) {
              var currentValue = state.backlinks[i].value;
        
              backlinkBuckets[currentValue-1].push(state.backlinks[i]);
        
              state.backlinks[i].secondaryPositionStatus = currentValue * -1 - 5;
        
              secondaryState[currentValue-1]++;
        
              state.backlinks[currentValue + numElements - 1].highlight = HIGHLIGHT_BLUESHADES[secondaryState[currentValue - 1]];
        
              state.lineNo = [2, 3];
              state.status = '将'.replace("{curVal}", currentValue);
        
              StateHelper.updateCopyPush(statelist, state);
              secondaryStatelist.push(secondaryState.slice()); // copy the array and push it into the secondary statelist
            }
        
            for (var i = 0, j = 0; i < maxCountingSortElementValue; ) {
              if (backlinkBuckets[i].length == 0) {
                i++;
                continue;
              }
        
              state.backlinks[j++] = backlinkBuckets[i].shift();
            }
        
            for (var i = 0; i < numElements; i++) {
              var currentValue = state.backlinks[i].value;
        
              state.backlinks[i].secondaryPositionStatus = POSITION_USE_PRIMARY;
        
              secondaryState[currentValue - 1]--;
        
              state.backlinks[currentValue + numElements - 1].highlight = HIGHLIGHT_BLUESHADES[secondaryState[currentValue - 1]];
        
              state.lineNo = [4, 5, 6, 7];
              state.status = '重新存储元素&nbsp;{curVal}, 并且将值为 {curVal} 的计数减&nbsp;1.'.replace("{curVal}", currentValue);
        
              StateHelper.updateCopyPush(statelist, state);
              secondaryStatelist.push(secondaryState.slice()); //copy the array and push it into the secondary statelist
            }
        
            state.barsCountOffset = 0;
        
            for (var i = 1; i <= maxCountingSortElementValue; i++) {
              state.entries.pop();
              state.backlinks.pop();
            }
        
            state.lineNo = 0;
            state.status = '排序完成!';
            StateHelper.updateCopyPush(statelist, state);
            secondaryStatelist.push(null); //copy the array and push it into the secondary statelist
        
            this.play(callback);
            return true;
          }
        
          this.randomizedQuickSort = function(callback) {
            quickSortUseRandomizedPivot = true;
            quickSortStart();
        
            this.play(callback);
            return true;
          }
        
          this.quickSort = function(callback) {
            quickSortUseRandomizedPivot = false;
            quickSortStart();
        
            this.play(callback);
            return true;
          }
        
          var quickSortStart = function() {
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[statelist.length - 1]);
        
            populatePseudocode([
            'for each (unsorted) partition',
            (quickSortUseRandomizedPivot) ? 'randomly select pivot, swap with first element' : 'set first element as pivot',
            '  storeIndex = pivotIndex + 1',
            '  for i = pivotIndex + 1 to rightmostIndex',
            '    if element[i] < element[pivot]',
            '      swap(i, storeIndex); storeIndex++',
            '  swap(pivot, storeIndex - 1)'
            ]);
        
            quickSortSplit(state, 0, numElements - 1);
        
            state.lineNo = 0;
            state.status = '排序完成!';
        
            for (var i = 0; i < numElements; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE; //unhighlight everything
            StateHelper.updateCopyPush(statelist, state);
          }
        
          var quickSortSplit = function(state, startIndex, endIndex) { //startIndex & endIndex inclusive
            state.status = '进行拆分&nbsp;[{partition}] (下标从&nbsp;{startIndex} 到 {endIndex} ，两边皆包括).'
                          .replace("{partition}", state.backlinks.slice(startIndex, endIndex + 1).map(function(d) {
                             return d.value;
                          }))
                          .replace("{startIndex}", startIndex).replace("{endIndex}", endIndex);
            state.lineNo = 1;
        
            if (startIndex > endIndex)
              return;
        
            if (startIndex == endIndex) {
              state.status += ' 因为拆分大小 == 1，在拆分项中的元素一定会在排序过后的位置。';
              state.backlinks[startIndex].highlight = HIGHLIGHT_SORTED;
              StateHelper.updateCopyPush(statelist, state);
              return;
            }
        
            var middleIndex = quickSortPartition(state, startIndex, endIndex);
            quickSortSplit(state, startIndex, middleIndex - 1);
            quickSortSplit(state, middleIndex + 1, endIndex);
          }
        
          var quickSortPartition = function(state, startIndex, endIndex) {
        
            var pivotIndex;
            if (quickSortUseRandomizedPivot) {
        
              pivotIndex = generateRandomNumber(startIndex, endIndex);
        
              state.status += ' 随机选取&nbsp;{pivot} (下标为&nbsp;{index}) 作为轴心点'.replace("{pivot}", state.backlinks[pivotIndex].value).replace("{index}", pivotIndex);
              state.lineNo = [1, 2];
        
              state.backlinks[pivotIndex].highlight = HIGHLIGHT_PIVOT;
              StateHelper.updateCopyPush(statelist, state);
        
              if (pivotIndex != startIndex) {
                state.status = '交换轴心点&nbsp;({pivot}}, 下标为&nbsp;{index}) 和第一个元素&nbsp;({first}, 下标 {firstIndex}). (存储下标= {storeIndex}.)'.replace("{pivot}", state.backlinks[pivotIndex].value).replace("{index}", pivotIndex)
                      .replace("{first}", state.backlinks[startIndex].value).replace("{firstIndex}", startIndex).replace("{storeIndex}", (startIndex + 1));
        
                state.lineNo = [2, 3];
        
                EntryBacklinkHelper.swapBacklinks(state.backlinks, pivotIndex, startIndex);
                pivotIndex = startIndex;
                StateHelper.updateCopyPush(statelist, state);
              }
            }
            else {
              pivotIndex = startIndex;
        
              state.status += ' 选择 {pivot} 作为轴心点. (存储下标&nbsp;= {storeIndex}.)'.replace("{pivot}", state.backlinks[pivotIndex].value).replace("{storeIndex}", (startIndex + 1));
              state.lineNo = [1, 2, 3];
        
              state.backlinks[pivotIndex].highlight = HIGHLIGHT_PIVOT;
              StateHelper.updateCopyPush(statelist, state);
            }
        
            var storeIndex = pivotIndex + 1;
            var pivotValue = state.backlinks[pivotIndex].value;
        
            for (var i = storeIndex; i <= endIndex; i++) {
              state.status = '检查是否&nbsp;{val} &lt; {pivot} (轴心点).'.replace("{val}", state.backlinks[i].value).replace("{pivot}", pivotValue);
              state.lineNo = [4, 5];
        
              state.backlinks[i].highlight = HIGHLIGHT_SPECIAL;
              StateHelper.updateCopyPush(statelist, state);
              if (state.backlinks[i].value < pivotValue) {
                state.status = '{val} &lt; {pivot} (轴心点) 是真（True）. 将下标为&nbsp;{idx} (值&nbsp;= {val}) 和在存储下标的元素&nbsp;(下标&nbsp;= {storeIdx}, 值 = {storeVal}) 进行交换. (交换过后在存储下标上的值&nbsp;= {newStoreIdx}).'.replace("{val}", state.backlinks[i].value).replace("{pivot}", pivotValue)
                      .replace("{idx}", i).replace("{storeIdx}", storeIndex).replace("{storeVal}", state.backlinks[storeIndex].value).replace("newStoreIdx", (storeIndex + 1));
                state.lineNo = [4, 6];
        
                if (i != storeIndex) {
                  EntryBacklinkHelper.swapBacklinks(state.backlinks, storeIndex, i);
                  StateHelper.updateCopyPush(statelist, state);
                }
        
                state.backlinks[storeIndex].highlight = HIGHLIGHT_LEFT;
                storeIndex++;
              }
              else {
                state.backlinks[i].highlight = HIGHLIGHT_RIGHT;
              }
            }
            state.status = '遍历完成。<br>';
            state.lineNo = 4;
            StateHelper.updateCopyPush(statelist, state);
            if (storeIndex - 1 != pivotIndex) {
              state.status = '将轴心点（下标= {pivotIdx}, 值 = {pivot}) 和在存储下标-1 的元素 (下标 = {newIdx}, 值 = {newVal}) 进行交换）<br>'.replace("{pivotIdx}", pivotIndex).replace("{pivot}", pivotValue)
                    .replace("{newIdx}", (storeIndex - 1)).replace("{newVal}", state.backlinks[storeIndex - 1].value);
              state.lineNo = 7;
              EntryBacklinkHelper.swapBacklinks(state.backlinks, storeIndex - 1, pivotIndex);
              StateHelper.updateCopyPush(statelist, state);
            }
        
            state.status = '现在轴心点已经在排序过后的位置<br>';
            state.lineNo = 7;
        
            for (var i = startIndex; i <= endIndex; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE; //unhighlight everything
            state.backlinks[storeIndex - 1].highlight = HIGHLIGHT_SORTED;
            StateHelper.updateCopyPush(statelist, state);
        
            return storeIndex - 1;
          }
        
          this.mergeSort = function(callback) {
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[0]);
        
            populatePseudocode([
            'split each element into partitions of size 1',
            'recursively merge adjacent partitions',
            '  for i = leftPartIdx to rightPartIdx',
            '    if leftPartHeadValue <= rightPartHeadValue',
            '      copy leftPartHeadValue',
            '    else: copy rightPartHeadValue' + ((this.computeInversionIndex) ? '; Increase InvIdx' : ""),
            'copy elements back to original array'
            ]);
        
            mergeSortInversionIndexCounter = 0;
        
            for (var i = 0; i < numElements; i++) {
              state.backlinks[i].highlight = HIGHLIGHT_RAINBOW[i];
            }
        
            state.status = '将数组拆分成每项只有一个元素（每个拆分项有一个独特的颜色）';
            status.lineNo = 1;
            StateHelper.updateCopyPush(statelist, state);
        
            this.mergeSortSplitMerge(state, 0, numElements);
        
            for (var i = 0; i < numElements; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE; //unhighlight everything
        
            state.status = '排序完成!';
            if (this.computeInversionIndex) {
              state.status += ' （倒置下标 = {idx}）<br>'.replace("{idx}", mergeSortInversionIndexCounter);
            }
        
            state.lineNo = 0;
            StateHelper.updateCopyPush(statelist, state);
        
            this.play(callback);
            return true;
          }
        
          this.mergeSortSplitMerge = function(state, startIndex, endIndex) { //startIndex inclusive, endIndex exclusive
            if (endIndex - startIndex <= 1)
              return;
        
            var middleIndex = Math.ceil((startIndex + endIndex) / 2);
            this.mergeSortSplitMerge(state, startIndex, middleIndex);
            this.mergeSortSplitMerge(state, middleIndex, endIndex);
            this.mergeSortMerge(state, startIndex, middleIndex, endIndex)
        
            // Copy array back
            state.status = '将新数组中的元素拷贝回原来的数组中。<br>';
            state.lineNo = 7;
        
            var duplicateBacklinks = new Array();
            for (var i = startIndex; i < endIndex; i++) {
              var newPosition = state.backlinks[i].secondaryPositionStatus;
              duplicateBacklinks[newPosition] = state.backlinks[i];
            }
        
            for (var i = startIndex; i < endIndex; i++) {
              state.backlinks[i] = duplicateBacklinks[i];
            }
        
            for (var i = startIndex; i < endIndex; i++) {
              state.backlinks[i].secondaryPositionStatus = POSITION_USE_PRIMARY;
              StateHelper.updateCopyPush(statelist, state);
            }
          }
        
          this.mergeSortMerge = function(state, startIndex, middleIndex, endIndex) {
            var leftIndex = startIndex;
            var rightIndex = middleIndex;
        
            var newHighlightColor = state.backlinks[startIndex].highlight;
        
            state.status = '现在将拆分项 [{partition1}] (下标从 {startIdx1} 到 {endIdx1}，两边都包括) 和 [{partition2}] 下标从 {startIdx2} 到 {endIdx2} ，两边都包括) 归并在一起。<br>'
                .replace('{partition1}', state.backlinks.slice(startIndex, middleIndex).map(function(d) {
                  return d.value;
                }))
                .replace("{startIdx1}", startIndex).replace("{endIdx1}", (middleIndex - 1))
                .replace("{partition2}", state.backlinks.slice(middleIndex, endIndex).map(function(d) {
                  return d.value;
                }))
                .replace("{startIdx2}", middleIndex).replace("{endIdx2}", (endIndex - 1));
            state.lineNo = 2;
        
            state.backlinks[leftIndex].highlight = makePaler(state.backlinks[leftIndex].highlight);
            state.backlinks[rightIndex].highlight = makePaler(state.backlinks[rightIndex].highlight);
            StateHelper.updateCopyPush(statelist, state);
        
            for (var i = startIndex; i < endIndex; i++) {
              // Note here we don't actually copy the elements into a new array, like in a usual mergesort.
              // This is left instead to the mergeSortSplitMerge to handle as it's easier there.
              // (We use the useSecondaryPostion property to overcome this lack-of-copying.)
              if (leftIndex < middleIndex && (rightIndex >= endIndex || state.backlinks[leftIndex].value <= state.backlinks[rightIndex].value)) {
                state.backlinks[leftIndex].highlight = newHighlightColor;
                state.backlinks[leftIndex].secondaryPositionStatus = i;
        
                if (rightIndex < endIndex) {
                  state.status = '因为 {leftPart} (左拆分) &lt;= {rightPart} (右拆分), 我们将&nbsp;{rightPart} 拷进新的数组。<br>'
                    .replace("{leftPart}", state.backlinks[leftIndex].value).replace("{rightPart}", state.backlinks[rightIndex].value);
                }
                else {
                  state.status = '因为右拆分是空的，我们将{leftPart} (左拆分) 拷贝进新的数组。'.replace("{leftPart}", state.backlinks[leftIndex].value);
                }
                state.lineNo = [3, 4, 5];
        
                leftIndex++;
                if (leftIndex != middleIndex)
                  state.backlinks[leftIndex].highlight = makePaler(state.backlinks[leftIndex].highlight);
        
                StateHelper.updateCopyPush(statelist, state);
              }
              else {
                state.backlinks[rightIndex].highlight = newHighlightColor;
                state.backlinks[rightIndex].secondaryPositionStatus = i;
        
                if (leftIndex < middleIndex) {
                  state.status = '<span style="font-size: 13px;">因为 {leftPart} (左拆分) &gt; {rightPart} (右拆分), 我们将&nbsp;{rightPart} 拷进新的数组。</span><br>'
                    .replace("{leftPart}", state.backlinks[leftIndex].value).replace("{rightPart}", state.backlinks[rightIndex].value);
                }
                else {
                  state.status = '因为左拆分是空的，我们将&nbsp;{rightPart}&nbsp; （右拆分）拷进新的数组。'.replace("{rightPart}", state.backlinks[rightIndex].value);
                }
        
                if (this.computeInversionIndex) {
                  mergeSortInversionIndexCounter += middleIndex - leftIndex;
                  state.status += '(我们将左拆分的大小 (= {sizeofleft}) 加至倒置下标 ({inversionidxcounter}).)<br>'
                    .replace("{sizeofleft}", (middleIndex - leftIndex)).replace("{inversionidxcounter}", mergeSortInversionIndexCounter);
                }
                else {
                  state.status += '可能出错了';
                }
                state.lineNo = [3, 6];
        
                rightIndex++;
                if (rightIndex != endIndex)
                  state.backlinks[rightIndex].highlight = makePaler(state.backlinks[rightIndex].highlight);
        
                StateHelper.updateCopyPush(statelist, state);
              }
            }
          }
        
          this.insertionSort = function(callback) {
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[0]);
        
            populatePseudocode([
            'mark first element as sorted',
            'for each unsorted element X',
            '  &#39;extract&#39; the element X',
            '  for j = lastSortedIndex down to 0',
            '    if current element j &gt; X',
            '      move sorted element to the right by 1',
            '    break loop and insert X here'
            ]);
        
            // First element always sorted
            state.lineNo = 1;
            // Mark the first element ({firstVal}) as sorted.
            state.status = '将第一个元素&nbsp;({firstVal}) 标记为已经排序过。'.replace("{firstVal}", state.backlinks[0].value);
            state.backlinks[0].highlight = HIGHLIGHT_SORTED;
            StateHelper.updateCopyPush(statelist, state);
        
            for (var i = 1; i < numElements; i++) {
              // Highlight first unsorted element
              state.lineNo = [2, 3];
              // Extract the first unsorted element ({val}).
              state.status = '提取第一个没有排序过的元素&nbsp;({val})。'.replace("{val}", state.backlinks[i].value);
              state.backlinks[i].highlight = HIGHLIGHT_SPECIAL;
              state.backlinks[i].secondaryPositionStatus = POSITION_USE_SECONDARY_IN_DEFAULT_POSITION;
              StateHelper.updateCopyPush(statelist, state);
        
              for (var j = i-1; j >= 0; j--) {
                state.lineNo = 4;
                // Figure where to insert extracted element.
                // Comparing with sorted element {val}.
                state.status = '找出插入提取元素的地方；和已经排序过的元素 {val} 比较。'.replace("{val}", state.backlinks[j].value);;
                state.backlinks[j].highlight = HIGHLIGHT_STANDARD;
                StateHelper.updateCopyPush(statelist, state);
        
                if (state.backlinks[j].value > state.backlinks[j+1].value) {
                  state.lineNo = [5, 6];
                  // {val1} > {val2} is true.
                  // Hence move current sorted element ({val1}) to the right by 1.
                  state.status = '{val1} &gt; {val2} 成立（True）, &nbsp;则将现在已经排序过的元素</span><span style="white-space: normal;">({val1}) 向右移动1格。'.replace("{val1}", state.backlinks[j].value).replace("{val2}", state.backlinks[j+1].value);
                  EntryBacklinkHelper.swapBacklinks(state.backlinks, j, j+1);
                  StateHelper.updateCopyPush(statelist, state);
                  state.backlinks[j+1].highlight = HIGHLIGHT_SORTED;
                }
                else {
                  state.lineNo = 7;
                  // {val1} > {val2} is false.
                  // Insert extracted element at current position.
                  state.status = '{val1} &gt; {val2} 不成立（False）, 在现有位置上插入一个元素。'.replace("{val1}", state.backlinks[j].value).replace("{val2}", state.backlinks[j+1].value);
                  state.backlinks[j].highlight = HIGHLIGHT_SORTED;
                  state.backlinks[j+1].secondaryPositionStatus = POSITION_USE_PRIMARY;
                  state.backlinks[j+1].highlight = HIGHLIGHT_SORTED;
                  StateHelper.updateCopyPush(statelist, state);
                  break;
                }
              }
        
              if (state.backlinks[0].secondaryPositionStatus == POSITION_USE_SECONDARY_IN_DEFAULT_POSITION) {
                state.lineNo = 4;
                // At beginning of array (nothing to compare).
                // Hence insert extracted element at current position.
                state.status = '在数组的最开始（没有东西可以比较），则在现有位置上插入元素。';
                state.backlinks[0].secondaryPositionStatus = POSITION_USE_PRIMARY;
                state.backlinks[0].highlight = HIGHLIGHT_SORTED;
                StateHelper.updateCopyPush(statelist, state);
              }
            }
        
            for (var i = 0; i < numElements; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE; //unhighlight everything
            state.lineNo = 0;
            // The array/list is now sorted.
            state.status = '排序完成!';
            StateHelper.updateCopyPush(statelist, state);
        
            this.play(callback);
            return true;
          }
        
          this.selectionSort = function(callback) {
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[0]);
        
            populatePseudocode([
            'repeat (numOfElements - 1) times',
            '  set the first unsorted element as the minimum',
            '  for each of the unsorted elements',
            '    if element < currentMinimum',
            '      set element as new minimum',
            '  swap minimum with first unsorted position'
            ]);
        
            for (var i = 0; i < numElements-1; i++) {
              var minPosition = i;
        
              // Iteration {iteration}: Set {val} as the current minimum.
              // Then iterate through the rest to find the true minimum.
              state.status = '循环 {iteration}: 把现在的最小值设置成为&nbsp;{val} , 然后通过遍历剩下的没有排序过的元素来找到真正的最小值。'.replace("{iteration}", (i+1)).replace("{val}", state.backlinks[i].value);
              state.lineNo = [1, 2, 3];
              state.backlinks[minPosition].highlight = HIGHLIGHT_SPECIAL;
        
              StateHelper.updateCopyPush(statelist, state);
        
              for (var j = i+1; j < numElements; j++) {
                // Check if {val} is smaller than the current minimum ({minVal}).
                state.status = '检查是否&nbsp;{val} 小于现在的最小值&nbsp;({minVal})。'.replace("{val}", state.backlinks[j].value).replace("{minVal}", state.backlinks[minPosition].value);
                state.lineNo = 4;
                state.backlinks[j].highlight = HIGHLIGHT_STANDARD;
                StateHelper.updateCopyPush(statelist, state);
        
                state.backlinks[j].highlight = HIGHLIGHT_NONE;
        
                if (state.backlinks[j].value < state.backlinks[minPosition].value) {
                  state.status = '将 {val} 设为新的最小值。'.replace("{val}", state.backlinks[j].value);
                  state.lineNo = 5;
                  state.backlinks[minPosition].highlight = HIGHLIGHT_NONE;
                  state.backlinks[j].highlight = HIGHLIGHT_SPECIAL;
        
                  minPosition = j;
                  StateHelper.updateCopyPush(statelist, state);
                }
              }
        
              if (minPosition != i) { // Highlight the first-most unswapped position, if it isn't the minimum
                // Set {val} as the new minimum.
                state.status = '交换最小的元素&nbsp;({minVal}) 和第一个没有排序过的元素&nbsp;({element})。'.replace("{minVal}", state.backlinks[minPosition].value).replace("{element}", state.backlinks[i].value);
                state.lineNo = 6;
                state.backlinks[i].highlight = HIGHLIGHT_SPECIAL;
                StateHelper.updateCopyPush(statelist, state);
        
                EntryBacklinkHelper.swapBacklinks(state.backlinks, minPosition, i);
                StateHelper.updateCopyPush(statelist, state);
              }
              else {
                // As the minimum is the first unsorted element, no swap is necessary.
                state.status = '因为最小值是第一个非排序过的元素，没有必要进行交换。';
                state.lineNo = 6;
                StateHelper.updateCopyPush(statelist, state);
              }
        
              // {val} is now considered sorted.
              state.status = '现在 {val} 被认为是排序过的了。'.replace("{val}", state.backlinks[i].value);
              state.backlinks[minPosition].highlight = HIGHLIGHT_NONE;
              state.backlinks[i].highlight = HIGHLIGHT_SORTED;
              StateHelper.updateCopyPush(statelist, state);
            }
        
            for (var i = 0; i < numElements; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE; // un-highlight everything
            // The array/list is now sorted.
            // (After all iterations, the last element will naturally be sorted.)
            state.status = '排序完成!' + '<br>' + '（所有循环过后，最后一个元素自然就被排序过了）';
            status.lineNo = 0;
            StateHelper.updateCopyPush(statelist, state);
        
            this.play(callback);
            return true;
          }
        
          this.bubbleSort = function(callback) {
            var numElements = statelist[0].backlinks.length;
            var state = StateHelper.copyState(statelist[0]);
            var swapCounter = 0;
        
            populatePseudocode([
            'do',
            '  swapped = false',
            '  for i = 1 to indexOfLastUnsortedElement-1',
            '    if leftElement > rightElement',
            '      swap(leftElement, rightElement)',
            '      swapped = true' + ((this.computeInversionIndex) ? '; <b>swapCounter</b>++' : ""),
            'while swapped'
            ]);
        
            var swapped;
            var indexOfLastUnsortedElement = numElements;
            do {
              swapped = false;
        
              // Set the swapped flag to false.
              // Then iterate from 1 to {endIdx} inclusive.
              state.status = '将已交换旗帜变量设置为假（False），然后从1到{endIdx} 循环（包括起始）。'.replace("{endIdx}", indexOfLastUnsortedElement-1);
              state.lineNo = [2, 3];
              StateHelper.updateCopyPush(statelist, state);
        
              for (var i = 1; i < indexOfLastUnsortedElement; i++) {
                state.backlinks[i-1].highlight = HIGHLIGHT_STANDARD;
                state.backlinks[i].highlight = HIGHLIGHT_STANDARD;
        
                // Checking if {val1} > {val2} and swap them if that is true.
                // The current value of swapped = {swapped}.
                state.status = '检查是否 {val1} &gt; {val2}; 如果是则交换元素。 (现在交换过后的值&nbsp;= {swapped})。'.replace("{val1}", state.backlinks[i-1].value).replace("{val2}", state.backlinks[i].value).replace("{swapped}", swapped);
                state.lineNo = 4;
                StateHelper.updateCopyPush(statelist, state);
        
                if (state.backlinks[i-1].value > state.backlinks[i].value) {
                  swapped = true;
        
                  // Swapping the positions of {val1} and {val2}.
                  // Set swapped = true.
                  state.status = '交换 {val1} 和 {val2} 的位置。将已交换旗帜变量设置为真（True）'.replace("{val1}", state.backlinks[i-1].value).replace("{val2}", state.backlinks[i].value);
                  if (this.computeInversionIndex) {
                    swapCounter++;
                    // For inversion index computation: Add 1 to swapCounter.
                    // The current value of swapCounter = {swapCounter}.
                    state.status += ' (倒置下标: 交换计数器加1. 现在交换计数器的值&nbsp;= {swapCounter}.)'.replace("{swapCounter}", swapCounter);
                  }
        
                  state.lineNo = [5, 6];
        
                  EntryBacklinkHelper.swapBacklinks(state.backlinks, i, i-1);
                  StateHelper.updateCopyPush(statelist, state);
                }
        
                state.backlinks[i-1].highlight = HIGHLIGHT_NONE;
                state.backlinks[i].highlight = HIGHLIGHT_NONE;
              }
        
              indexOfLastUnsortedElement--;
              state.backlinks[indexOfLastUnsortedElement].highlight = HIGHLIGHT_SORTED;
              if (swapped == false)
                // No swap is done in this pass.
                // We can terminate Bubble Sort now.
                state.status = '在这趟扫描过程中没有任何交换发生，现在可以终止冒泡排序。';
              else
                // Mark last unsorted element as sorted now.
                // As at least one swap is done in this pass, we continue.
                state.status = '将最后一个没有排序过的元素标记为已排序。因为在最近的一次扫描过程中至少有一次交换发生过，可以进行另一轮扫描。';
        
              state.lineNo = 7;
              StateHelper.updateCopyPush(statelist, state);
            }
            while (swapped);
        
            for (var i = 0; i < numElements; i++)
              state.backlinks[i].highlight = HIGHLIGHT_NONE; //un-highlight everything
        
            // The array/list is now sorted.
            state.status = '排序完成!';
            if (this.computeInversionIndex)
              // Inversion Index = {swapCounter}.
              state.status += ' (倒置下标&nbsp;= {swapCounter}.)'.replace("swapCounter", swapCounter);
        
            state.lineNo = 0;
            StateHelper.updateCopyPush(statelist, state);
        
            this.play(callback);
            return true;
          }
        
          this.clearPseudocode = function() { populatePseudocode([]); }
        
          var populatePseudocode = function(code) {
            var i = 1;
            for (; i <= 7 && i <= code.length; i++) {
              $("#code" + i).html(
                code[i - 1].replace(
                /^\s+/,
                function(m) { return m.replace(/\s/g, "&nbsp;"); }
                )
              );
            }
            for (; i <= 7; i++) {
              $("#code" + i).html("");
            }
          }
        
          //animation functions
          var drawCurrentState = function() {
            $('#progress-bar').slider("value", currentStep);
            drawState(currentStep);
            if (currentStep == (statelist.length-1)) {
              pause(); //in html file
              $('#play img').attr('src', '../assets/images/replay.png').attr('alt', 'replay').attr('title', 'replay');
            }
            else
              $('#play img').attr('src', '../assets/images/play.png').attr('alt', 'play').attr('title', 'play');
          }
        
          this.getAnimationDuration = function() { return transitionTime; }
        
          this.setAnimationDuration = function(x) {
            transitionTime = x;
            if (issPlaying) {
              clearInterval(animInterval);
              animInterval = setInterval(function() {
                drawCurrentState();
                if (currentStep < (statelist.length-1))
                  currentStep++;
                else
                  clearInterval(animInterval);
              }, transitionTime);
            }
          }
        
          this.getCurrentIteration = function() { return currentStep; }
        
          this.getTotalIteration = function() { return statelist.length; }
        
          this.forceNext = function() {
            if ((currentStep + 1) < statelist.length)
              currentStep++;
            drawCurrentState();
          }
        
          this.forcePrevious = function() {
            if ((currentStep-1) >= 0)
              currentStep--;
            drawCurrentState();
          }
        
          this.jumpToIteration = function(n) {
            currentStep = n;
            drawCurrentState();
          }
        
          this.play = function(callback) {
            issPlaying = true;
            drawCurrentState();
            animInterval = setInterval(function() {
              drawCurrentState();
              if (currentStep < (statelist.length-1))
                currentStep++;
              else {
                clearInterval(animInterval);
                if (typeof callback == 'function') callback();
              }
            }, transitionTime);
          }
        
          this.pause = function() {
            issPlaying = false;
            clearInterval(animInterval);
          }
        
          this.replay = function() {
            issPlaying = true;
            currentStep = 0;
            drawCurrentState();
            animInterval = setInterval(function() {
              drawCurrentState();
              if (currentStep < (statelist.length-1))
                currentStep++;
              else
                clearInterval(animInterval);
            }, transitionTime);
          }
        
          this.stop = function() {
            issPlaying = false;
            statelist = [statelist[0]]; //clear statelist to original state, instead of new Array();
            secondaryStatelist = [null];
            currentStep = 0;
            drawState(0);
          }
        }
        
        // sorting action
        var actionsWidth = 150;
        var statusCodetraceWidth = 420;
        
        var isCreateOpen = false;
        var isInsertOpen = false;
        var isRemoveOpen = false;
        var isSortOpen = false;
        
        function openCreate() {
          if (!isCreateOpen) {
            $('.create').fadeIn('fast');
            isCreateOpen = true;
          }
        }
        
        function closeCreate() {
          if (isCreateOpen) {
            $('.create').fadeOut('fast');
            $('#create-err').html("");
            isCreateOpen = false;
          }
        }
        
        function openInsert() {
          if (!isInsertOpen) {
            $('.insert').fadeIn('fast');
            isInsertOpen = true;
          }
        }
        
        function closeInsert() {
          if (isInsertOpen) {
            $('.insert').fadeOut('fast');
            $('#insert-err').html("");
            isInsertOpen = false;
          }
        }
        
        function openRemove() {
          if (!isRemoveOpen) {
            $('.remove').fadeIn('fast');
            isRemoveOpen = true;
          }
        }
        
        function closeRemove() {
          if (isRemoveOpen) {
            $('.remove').fadeOut('fast');
            $('#remove-err').html("");
            isRemoveOpen = false;
          }
        }
        
        function openSort() {
          if (!isSortOpen) {
            $('.sort').fadeIn('fast');
            isSortOpen = true;
          }
        }
        
        function closeSort() {
          if (isSortOpen) {
            $('.sort').fadeOut('fast');
            $('#sort-err').html("");
            isSortOpen = false;
          }
        }
        
        function hideEntireActionsPanel() {
          closeCreate();
          closeInsert();
          closeRemove();
          closeSort();
          hideActionsPanel();
        }
        
        
        
        // local
        $(function() {
          AbbreviateTitle();
          hideAllSubmenus();
          var eight_modes = ["Bubble", "Selection", "Insertion", "Merge", "Quick", "RandomizedQuick", "Counting", "Radix"];
          $('#title-'+eight_modes[Math.floor(Math.random()*8)]).click(); // randomly open one of the eight sorting algorithm mode every time
          $('#play').hide();
        
          d3.selectAll("#radix-sort-bucket-labels-collection span")
            .style({"left": function(d, i) {
                          return 17.5 + i * 65 + "px";
                  }});
          var sortMode = getQueryVariable("mode");
          if (sortMode.length > 0) {
             $('#title-' + sortMode).click();
          }
          var createArray = getQueryVariable("create");
          if (createArray.length > 0) {
            $('#userdefined-input').val(createArray);
            createList("userdefined");
          }
        
          $('#create').click(function() {
            closeInsert();
            closeRemove();
            closeSort();
            openCreate();
          });
        
          $('#insert').click(function() {
            closeCreate();
            closeRemove();
            closeSort();
            openInsert();
          });
        
          $('#remove').click(function() {
            closeCreate();
            closeInsert();
            closeSort();
            openRemove();
          });
        
          $('#sort').click(function() {
            closeCreate();
            closeInsert();
            closeRemove();
            openSort();
          });
        });
        
        //this viz-specific code
        var gw = new Sorting();
        
        const DEFAULT_DATA       = "3,44,38,5,47,15,36,26,27,2,46,4,19,50,48";
        const DEFAULT_COUNT_DATA = "2, 3, 8, 7, 1, 2, 2, 2, 7, 3, 9, 8, 2, 1, 4, 2, 4, 6, 9, 2";
        const DEFAULT_RADIX_DATA = "3221, 1, 10, 9680, 577, 9420, 7, 5622, 4793, 2030, 3138, 82, 2599, 743, 4127";
        
        // title changing
        function AbbreviateTitle() {
          $('#title-Bubble').text("BUB").attr('title', '冒泡排序');
          $('#title-Selection').text("SEL").attr('title', '选择排序');
          $('#title-Insertion').text("INS").attr('title', '插入排序');
          $('#title-Merge').text("MER").attr('title', '归并排序');
          $('#title-Quick').text("QUI").attr('title', '快速排序');
          $('#title-RandomizedQuick').text("R-Q").attr('title', '随机快速排序');
          $('#title-Counting').text("COU").attr('title', '计数排序');
          $('#title-Radix').text("RAD").attr('title', '基数排序');
        }
        $('#title-Bubble').click(function() {
          showStandardCanvas();
          $("#sort-bubble-merge-inversion").css("display", "");
          $('#current-action p').html('冒泡排序');
          changeSortType(gw.bubbleSort);
          AbbreviateTitle();
          $('#title-Bubble').text('冒泡排序');
        });
        $('#title-Selection').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('选择排序');
          changeSortType(gw.selectionSort);
          AbbreviateTitle();
          $('#title-Selection').text('选择排序');
        });
        $('#title-Insertion').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('插入排序');
          changeSortType(gw.insertionSort);
          AbbreviateTitle();
          $('#title-Insertion').text('插入排序');
        });
        $('#title-Merge').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $("#sort-bubble-merge-inversion").css("display", "");
          $('#current-action p').html('归并排序');
          AbbreviateTitle();
          changeSortType(gw.mergeSort);
          $('#title-Merge').text('归并排序');
        });
        $('#title-Quick').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('快速排序');
          changeSortType(gw.quickSort);
          AbbreviateTitle();
          $('#title-Quick').text('快速排序');
        });
        $('#title-RandomizedQuick').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('随机快速排序');
          changeSortType(gw.randomizedQuickSort);
          AbbreviateTitle();
          $('#title-RandomizedQuick').text('随机快速排序');
        });
        $('#title-Counting').click(function() {
          showStandardCanvas();
          $("#viz-counting-sort-secondary-canvas").show();
          hideAllSortingOptions();
          $('#current-action p').html('计数排序');
          changeSortType(gw.countingSort, DEFAULT_COUNT_DATA);
          AbbreviateTitle();
          $('#title-Counting').text('计数排序');
        });
        $('#title-Radix').click(function() {
          hideAllCanvases();
          $("#viz-radix-sort-canvas").show();
          hideAllSortingOptions();
          $('#current-action p').html('基数排序');
          changeSortType(gw.radixSort, DEFAULT_RADIX_DATA);
          AbbreviateTitle();
          $('#title-Radix').text('基数排序');
        });
        
        function changeSortType(newSortingFunction, customNumberList) {
          if (!customNumberList)
            $('#userdefined-input').val(DEFAULT_DATA);
          else
            $('#userdefined-input').val(customNumberList);
          createList('userdefined');
        
          if (isPlaying) stop();
          
          
          hideCodetracePanel();
          gw.clearPseudocode();
          gw.setSelectedSortFunction(newSortingFunction);
        }
        
        function createList(type) {
          if (isPlaying) stop();
          setTimeout(function() {
            if (gw.createList(type)) {
              $('#progress-bar').slider("option", "max", 0);
              closeCreate();
              isPlaying = false;
            }
          }, 500);
        }
        
        function sort(callback) {
          gw.computeInversionIndex = $('#sort-bubble-merge-inversion-checkbox').prop('checked');
          if (isPlaying) stop();
          setTimeout(function() {
            if (gw.sort(callback)) {
              $('#current-action').show();
              $('#progress-bar').slider("option", "max", gw.getTotalIteration()-1);
              triggerRightPanels();
              isPlaying = true;
            }
          }, 500);
        }
        
        // submenu stuff
        var lastSubmenuShown = null;
        
        function triggerSubmenu(which) {
          hideAllSubmenus();
          if (lastSubmenuShown == which) {
            lastSubmenuShown = null;
            return;
          }
        
          lastSubmenuShown = which;
        
          $(".create").css("bottom", "60px");
          if (which == "sorted") {
            $("#create-sorted-increasing").show();
            $("#create-sorted-decreasing").show();
          }
          else if (which == "nearly-sorted") {
            $("#create-nearly-sorted-increasing").show();
            $("#create-nearly-sorted-decreasing").show();
          }
        }
        
        function hideAllSubmenus() {
          $(".create").css("bottom", "92px");
          $("#create-sorted-increasing").hide();
          $("#create-sorted-decreasing").hide();
          $("#create-nearly-sorted-increasing").hide();
          $("#create-nearly-sorted-decreasing").hide();
        }
        
        // sort options
        function hideAllSortingOptions() {
          $("#sort-bubble-merge-inversion").css("display", "none");
        }
        
        // canvas
        function hideAllCanvases() {
          $("#viz-canvas").hide();
          $("#viz-counting-sort-secondary-canvas").hide();
          $("#viz-radix-sort-canvas").hide();
        }
        
        function showStandardCanvas() {
          $("#viz-canvas").show();
          $("#viz-counting-sort-secondary-canvas").hide();
          $("#viz-radix-sort-canvas").hide();
        }
        
        var exploreModeData = [];
        
        // This function will be called before entering E-Lecture Mode
        function ENTER_LECTURE_MODE() {
          exploreModeData = gw.currentNumList;
        }
        
        // This function will be called before returning to Explore Mode
        function ENTER_EXPLORE_MODE() {
          gw.loadNumberList(exploreModeData);
        }
        
        // Lecture action functions
        function SORT(mode) {
          hideSlide(function() {
            sort(showSlide);
          });
        }
        function CUSTOM_ACTION(action, data, mode) {}
        </script>
       <script type="text/javascript">
        jQuery(document).ready(function($) {     
            // Charts
            var xenonPalette = ['#68b828', '#7c38bc', '#0e62c7', '#fcd036', '#4fcdfc', '#00b19d', '#ff6264', '#f7aa47'];
            // Pageviews Visitors Chart
            showStandardCanvas();
            changeSortType(gw.insertionSort);
                $("#page-title").text('插入排序');
        });
        $('#title-Bubble').click(function() {
          showStandardCanvas();
          $("#sort-bubble-merge-inversion").css("display", "");
          $('#current-action p').html('冒泡排序');
          changeSortType(gw.bubbleSort);
          AbbreviateTitle();
          $('#title-Bubble').text('冒泡排序');
        });
        $('#title-Selection').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('选择排序');
          changeSortType(gw.selectionSort);
          AbbreviateTitle();
          $('#title-Selection').text('选择排序');
        });
        $('#title-Insertion').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('插入排序');
          changeSortType(gw.insertionSort);
          AbbreviateTitle();
          $('#title-Insertion').text('插入排序');
        });
        $('#title-Merge').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $("#sort-bubble-merge-inversion").css("display", "");
          $('#current-action p').html('归并排序');
          AbbreviateTitle();
          changeSortType(gw.mergeSort);
          $('#title-Merge').text('归并排序');
        });
        $('#title-Quick').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('快速排序');
          changeSortType(gw.quickSort);
          AbbreviateTitle();
          $('#title-Quick').text('快速排序');
        });
        $('#title-RandomizedQuick').click(function() {
          showStandardCanvas();
          hideAllSortingOptions();
          $('#current-action p').html('随机快速排序');
          changeSortType(gw.randomizedQuickSort);
          AbbreviateTitle();
          $('#title-RandomizedQuick').text('随机快速排序');
        });
        $('#title-Counting').click(function() {
          showStandardCanvas();
          $("#viz-counting-sort-secondary-canvas").show();
          hideAllSortingOptions();
          $('#current-action p').html('计数排序');
          changeSortType(gw.countingSort, DEFAULT_COUNT_DATA);
          AbbreviateTitle();
          $('#title-Counting').text('计数排序');
        });
        $('#title-Radix').click(function() {
          hideAllCanvases();
          $("#viz-radix-sort-canvas").show();
          hideAllSortingOptions();
          $('#current-action p').html('基数排序');
          changeSortType(gw.radixSort, DEFAULT_RADIX_DATA);
          AbbreviateTitle();
          $('#title-Radix').text('基数排序');
        });
        
      
    </script>        

</body>

</html>